import * as cheerio from "cheerio";
import { mean, median, std } from "mathjs";
import logger from "../logger.js";
import { EventEmitter } from 'events';
import sharp from "sharp";

const XHS_URL = "https://www.xiaohongshu.com";
const MIN_GOOD_FEEDS_COUNT = 3;
const MIN_HOT_FEEDS_LIKE_COUNT = 10000;

export default class XhsAgent extends EventEmitter {
  constructor(page, config) {
    super();
    this.page = page;
    this.started = false;
    this.config = config;
    logger.info("class XhsSpy initialized");
  }

  async start() {
    try {
      this.started = true;

      await this.page.goto(XHS_URL);

      // Check if already logged in
      const isLoggedIn = await this.isLoggedIn();
      if (isLoggedIn) {
        logger.info("Already logged in.");
      } else {
        await this.loginByQrcodeImgs();
      }

    } catch (error) {
      logger.error(error);
      throw error;
    }
  }

  async gotoPage(url) {
    try {
      if (!this.started) {
        throw new Error("Agent not started");
      }
      if (await this.isLoggedIn()) {
        await this.page.goto(url);
        await this.humanDelay();
      } else {
        throw new Error("Not logged in");
      }
    } catch (error) {
      throw error;
    }
  }
  async waitForPageLoad() {
    try {
      await this.page.waitForLoadState('domcontentloaded');
    } catch (error) {
      logger.error('Failed to wait for page load:', error);
      throw error;
    }
  }

  async run() {
    try {
      while (this.started) {
        await this.page.waitForTimeout(1000); // Wait for 1 second
        const keywords = this.config["xhs"]["keywords"];
        for (const query of keywords) {
          await this.runTask(query); 
        }
        await this.humanDelay();    
      }  
    } catch (error) {
      throw error;
    }

  }
  async loginByQrcodeImgs() {
    try {
      logger.info("Starting login by QR code...");
      // Wait for the QR code to appear
      await this.page.waitForSelector(".qrcode-img", { timeout: 0 });
      logger.debug("QR code detected.");
  
      // Wait for the QR code to disappear
      await this.page.waitForSelector(".qrcode-img", {
        state: "detached",
        timeout: 0,
      });
      // logger.debug("Login successful, QR code disappeared.");
        
    } catch (error) {
      throw error;
    }
  }

  async stop() {
    this.started = false;
  }

  async runTask(query) {
    try {
      if (query) {
        await this.page.getByRole('textbox', { name: '搜索小红书' }).click();
        await this.page.getByRole('textbox', { name: '搜索小红书' }).fill(query);
        await this.page.getByRole('textbox', { name: '搜索小红书' }).press('Enter');   

        await this.page.waitForTimeout(2000);   
        await this.selectDropdownOption('svg.filter-icon', '筛选');
        // await this.page.locator('svg.filter-icon').click();
        await this.page.locator('div').filter({ hasText: /^最多点赞$/ }).click();
        await this.page.locator('div').filter({ hasText: /^一周内$/ }).click();
        await this.page.getByText('未看过').click();

        await this.page.waitForTimeout(2000);

      } else {
        await this.gotoPage(XHS_URL);;
      }
    // console.log("Current URL:", currentUrl);

    const feeds = [];

    let loopCount = 0;
    if (query === '') {
      loopCount = 5;
    } else {
      loopCount = 3;
    }
    for (let i = 0; i < loopCount; i++) {
      await this.page.waitForTimeout(3000);    
      // Get all section elements
      const sections = await this.page.locator("section").all();
      logger.debug(`Found ${sections.length} sections`);

      // Iterate through each section and extract the like count
      for (const section of sections) {
        const feed = await this.parseFeedSection(section, query);
        if (!feed) {
          continue;
        }
        if (this.isFeedHot(feed) || (feed.keywords != "" && feeds.length < MIN_GOOD_FEEDS_COUNT)) {
          if (this.isFeedDuplicated(feed, feeds)) {
            logger.debug(`Duplicate feed: ${JSON.stringify(feed, null, 2)}`);
            continue;
          }
          feeds.push(feed);
        }
      }

      await this.scrollDownPage();
      await this.humanDelay();
    }   

      for (let feed of feeds) {        
        if (await this.isFeedOpened(feed.feed_url)) {
          continue;
        }
        // open feed url
        const feed_content = await this.gotoFeedUrl(feed.feed_url);
        if (feed_content) {
          feed = {...feed, feed_content };
          logger.debug(`Parsed feed: ${JSON.stringify(feed, null, 2)}`);
        }

        if (this.isFeedBlocked(feed)) {
          logger.debug(`Blocked feed: ${JSON.stringify(feed, null, 2)}`);
          continue;
        }

        // open profile url
        const profile = await this.gotoFeedProfile(feed.profile_url);
        if (profile) {
          const profile_likes_stats = this.analyzeProfileStats(profile);
          feed = { ...feed, profile_likes_stats };
          logger.debug(`Parsed feed: ${JSON.stringify(feed, null, 2)}`);
        }
        this.emit("hot-feed", feed);
      }

    } catch (error) {
      logger.error(error);
      // throw error;
    }
    // console.log("Restored to the original page:", currentUrl);
  }

  isFeedDuplicated(feed, feeds) {
    // 检查标题是否重复
    const isDuplicated = feeds.some(existingFeed => existingFeed.feed_url === feed.feed_url);
    return isDuplicated;
  }

  isFeedHot(feed) {
    return feed && feed.like_count >= MIN_HOT_FEEDS_LIKE_COUNT;
  }

  isFeedBlocked(feed) {
    const BLOCKED_WORDS = this.config["xhs"]["blocked_list"];
    if (!feed || !feed.feed_content?.tags) return false;
    return feed.feed_content.tags.some(tag => 
      BLOCKED_WORDS.some(word => tag.toLowerCase().includes(word))
    );
  }
  
  extractBaseUrl(url) {
    const queryIndex = url.indexOf('?');
    return queryIndex === -1 ? url : url.slice(0, queryIndex);
  }

  async isFeedOpened(feedUrl) {
    try {
      const feedBaeUrl = this.extractBaseUrl(feedUrl);
      const remote_endpoint = this.config["remote_host_url"] + "/api/hotfeed?feed_url=" + feedBaeUrl;
      const response = await fetch(remote_endpoint, { method: 'GET'});
      if (response.status === 404) {
        logger.debug('Resource not found');
        return false;
      }
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return true;
  } catch (error) {
      throw error;
    }
  }

  async scrollDownPage() {
    try {
      if (!this.started) {
        throw new Error("Agent not started");
      }
      if (await this.isLoggedIn()) {
        await this.page.evaluate(() => {
          window.scrollTo(0, document.body.scrollHeight);
        });
      } else {
        throw new Error("Not logged in");
      }
    } catch (error) {
      throw error;
    }
  }
  // 随机延迟（模拟人工操作）
  async humanDelay(baseSeconds = 5) {
    await this.page.waitForTimeout(baseSeconds * 1000 + Math.random() * baseSeconds * 1000);
  }

  analyzeProfileStats(profile) {
    // 收集所有 like_count
    const likes_in_profile = profile.map((item) => item.like_count);
    logger.debug("Likes in profile:", likes_in_profile);

    // 计算统计信息
    const maxValue = Math.max(...likes_in_profile);
    const minValue = Math.min(...likes_in_profile);
    const meanValue = Math.round(mean(likes_in_profile));
    const medianValue = Math.round(median(likes_in_profile));
    const stdDeviation = Math.round(std(likes_in_profile));

    // 打印统计信息
    logger.debug("Feed Statistics:");
    logger.debug("Max Value:", maxValue);
    logger.debug("Min Value:", minValue);
    logger.debug("Mean Value:", meanValue);
    logger.debug("Median Value:", medianValue);
    logger.debug("Standard Deviation:", stdDeviation);

    // 返回统计结果
    return {
      max: maxValue,
      min: minValue,
      mean: meanValue,
      median: medianValue,
      std: stdDeviation,
    };
  }

  async parseFeedSection(section, query) {
    try {
      // Extract the HTML content of the section
      const sectionHtml = await section.evaluate((node) => node.outerHTML);

      // Parse the HTML using cheerio
      const $ = cheerio.load(sectionHtml);

      let likeCount = 0;
      let title = "";
      let authorName = "";
      let profileUrl = "";
      let feedUrl = "";
      let imageUrl = "";
      let hasPlayIcon = false;

      if (query) {
        likeCount = $("span.count").text().trim();
        title = $('a.title span').text() || '';

        authorName = $('a.author .name span').text();;
        profileUrl = $('a.author').attr('href');;

        let searchResultUrl = $("section a:has(> img)").eq(0).attr("href");
        if (!searchResultUrl) {
          return null;
        }
        feedUrl = searchResultUrl.replace('search_result', 'explore')

        imageUrl = $('a.cover.mask.ld img').attr('src');
        hasPlayIcon = $('.play-icon').length > 0;        
       
      } else {
         // Extract the text value of <span class="count">
         likeCount = $("span.count").text().trim();
         title = $(".footer .title span").text().trim();
         authorName = $(".footer .author-wrapper span.name").text().trim();
         profileUrl = $(".footer .author-wrapper a").attr("href");
         feedUrl = $("section a:has(> img)").eq(0).attr("href");
         imageUrl = $('a.cover.mask.ld img').attr('src');
         hasPlayIcon = $('.play-icon').length > 0;        
      }

      // Return the extracted like count
      return {
        author_name: authorName,
        title,
        platform: "xhs",
        keywords: [query],
        feed_url: XHS_URL + feedUrl,
        profile_url: XHS_URL + profileUrl,
        image_url: imageUrl,
        video: hasPlayIcon,
        image_base64: await this.fetchAndEncodeImage(imageUrl),
        like_count: this.parseChineseNumber(likeCount), // Convert to integer, default to 0 if invalid
      };
    } catch (error) {
      logger.error("Failed to extract content for a section:", error);
      throw error;
    }
  }

  async gotoFeedUrl(feedUrl) {
    try {
      await this.gotoPage(feedUrl);

      const noteContent = await this.page.locator("div.note-content").all();
      const noteContentHtml = await noteContent[0].evaluate((node) => node.outerHTML);
      const $ = cheerio.load(noteContentHtml);

      // 提取标题
      const title = $('#detail-title').text().trim();

      // 提取 .note-text 下的所有非空 span 内容
      const spans = [];
      $('.note-text span').each((i, el) => {
        const text = $(el).text().trim();
        if (text) {
          spans.push(text);
        }
      });

      // 用 join() 连接所有 span 文本
      const text = spans.join(' ');

      // 提取所有标签
      const tags = [];
      $('#detail-desc .tag').each((i, el) => {
        tags.push($(el).text().trim());
      });

      return {
        title,
        text,
        tags,
      };

    } catch (error) {
      throw error;
    }
  }

  async gotoFeedProfile(profileUrl) {
    try {
      await this.gotoPage(profileUrl);

      const sections = await this.page.locator("section").all();

      const results = [];
      for (const section of sections) {
        const likeCount = await this.extractLikeCount(section);
        if (likeCount) {
          results.push(likeCount);
        }
      }
      return results;
    } catch (error) {
      throw error;
    }
  }

  async extractLikeCount(section) {
    try {
      // Extract the HTML content of the section
      const sectionHtml = await section.evaluate((node) => node.outerHTML);

      // Parse the HTML using cheerio
      const $ = cheerio.load(sectionHtml);

      // Extract the text value of <span class="count">
      const title = $(".footer .title span").text().trim();
      const likeCountText = $("span.count").text().trim();
      // console.log("Like count text:", likeCountText);
      const likeCount = this.parseChineseNumber(likeCountText); // Convert to integer, default to 0 if invalid

      return {
        title,
        like_count: likeCount,
      };
    } catch (error) {
      logger.error("Failed to extract like count:", error);
      throw error;
    }
  }

  async fetchAndEncodeImage(imageUrl) {
    try {
      const response = await fetch(imageUrl);
      const buffer = await response.arrayBuffer();
      const resizedBuffer = await sharp(buffer).resize(250, 300).toBuffer();
      return 'data:image/png;base64,'+ resizedBuffer.toString('base64');
    } catch (error) {
      logger.error('Failed to fetch and encode image:', error);
      return null;
    }
  }

  parseChineseNumber(chineseNumber) {
    let number = 0;

    if (chineseNumber.includes("万")) {
      // 如果包含 "万"，将其转换为数字
      number = parseFloat(chineseNumber.replace("万", "")) * 10000;
    } else {
      // 如果不包含 "万"，直接转换为整数
      number = parseInt(chineseNumber, 10) || 0;
    }

    return number;
  }

  async isLoggedIn() {
    try {
      await this.waitForPageLoad(); 

      const buttons = await this.page.getByRole('button', { name: '登录' }).count();
      // console.log(`Number of '登录' buttons: ${buttons}`);
      if (buttons > 0) {
        return false;
      }
      return true;
      // Additional check for logged-in specific elements
    } catch (error) {
      logger.error("Failed to check login status:", error);
      return false;
    }
  }

  async selectDropdownOption(selector, optionText) {
    try {
      await this.page.locator(selector).click();
      await this.page.locator(`text=${optionText}`).click();
    } catch (error) {
      logger.error('Failed to select dropdown option:', error);
      throw error;
    }
  }
}
